﻿Imports System.Web
Imports System.Text

' DEPENDENCIES:
'   Javascript
'   - jquery
'   - jquery.validate
'   - jquery.form

'   CSS
'   - bfmvc.css


Namespace AboutDNN.DNNMVC


    Public Class HtmlHelper

        Public Shared Function HtmlEncode(ByVal data As String) As String
            Return HttpUtility.HtmlEncode(data)
        End Function

        Public Shared Function HtmlDecode(ByVal data As String) As String
            Return HttpUtility.HtmlDecode(data)
        End Function

        Public Shared Function TextToHtml(ByVal data As String) As String
            Dim n = data
            n = HtmlHelper.HtmlEncode(n)
            n = n.Replace(vbCrLf, "<br />")
            n = n.Replace(vbLf, "<br />")
            n = n.Replace(vbCr, "<br />")
            n = n.Replace(vbNewLine, "<br />")
            Return n
        End Function

        Public Shared Function BeginForm() As TagBuilder
            Return New TagBuilder("form", RenderMode.openTagOnly)
        End Function

        Public Shared Function EndForm() As String
            Return "</form>"
        End Function

        Public Shared Function DIV() As TagBuilder
            Return New TagBuilder("div")
        End Function

        Public Shared Function DIV(ByVal content As String) As TagBuilder
            Return New TagBuilder("div").InnerHtml(content)
        End Function

        Public Shared Function SPAN() As TagBuilder
            Return New TagBuilder("span")
        End Function

        Public Shared Function SPAN(ByVal content As String) As TagBuilder
            Return New TagBuilder("span").InnerHtml(content)
        End Function

        Public Shared Function TextBox(ByVal name As String) As TagBuilder
            Return New TagBuilder("input").IDandName(name).type(InputTypes.text)
        End Function

        Public Shared Function File() As TagBuilder
            Return New TagBuilder("input").type(InputTypes.file)
        End Function

        Public Shared Function File(ByVal name As String) As TagBuilder
            Return New TagBuilder("input").IDandName(name).type(InputTypes.file)
        End Function

        Public Shared Function Password(ByVal name As String) As TagBuilder
            Return New TagBuilder("input").IDandName(name).type(InputTypes.password)
        End Function

        Public Shared Function TextArea(ByVal name As String) As TagBuilder
            Return New TagBuilder("textarea").IDandName(name)
        End Function

        Public Shared Function Hidden(ByVal name As String) As TagBuilder
            Return New TagBuilder("input").IDandName(name).type(InputTypes.hidden)
        End Function

        Public Shared Function Submit(ByVal text As String) As TagBuilder
            Return Submit(text, True)
        End Function

        Public Shared Function Submit(ByVal text As String, ByVal validate As Boolean) As TagBuilder
            Return Button(text).onclick(JSHelper.Submit.CancelEvent.ToString)
        End Function

        Public Shared Function Submit(ByVal text As String, ByVal ParamArray validateInputNames() As String) As TagBuilder
            Return Button(text).onclick(JSHelper.Submit(True, validateInputNames).ToString)
        End Function

        Public Shared Function Button(ByVal text As String) As TagBuilder
            Return New TagBuilder("button").InnerText(text)
        End Function

        Public Shared Function Label(ByVal forName As String, ByVal labelText As String) As TagBuilder
            Return New TagBuilder("label").for(forName).InnerText(labelText).AddClass("fieldLabel")
        End Function

        Public Shared Function Checkbox(ByVal name As String, ByVal value As Boolean) As String
            Return Checkbox(name, value, String.Empty, String.Empty)
        End Function

        Public Shared Function Checkbox(ByVal name As String, ByVal value As Boolean, ByVal cssclass As String) As String
            Return Checkbox(name, value, cssclass, String.Empty)
        End Function

        Public Shared Function Checkbox(ByVal name As String, ByVal value As Boolean, ByVal cssclass As String, ByVal label As String) As String
            Dim chkname = name & "_chk"
            Dim chk = New TagBuilder("input").IDandName(chkname).type(InputTypes.checkbox)
            Dim hdn = New TagBuilder("input").IDandName(name).type(InputTypes.hidden)
            ' Figure out the value
            If value Then
                chk.checked(True)
                hdn.value("true")
            Else
                chk.checked(False)
                hdn.value("false")
            End If
            ' Add the label
            If String.IsNullOrEmpty(label) = False Then
                chk.Label(label)
            End If
            ' Add the class if asked
            If String.IsNullOrEmpty(cssclass) = False Then
                chk.AddClass(cssclass)
            End If
            ' Wireup events
            chk.onclick("jQuery('#" & name & "').val(this.checked);")
            ' Return the value
            Return chk.ToString & hdn.ToString
        End Function

        Public Shared Function Radio(ByVal name As String, ByVal value As String) As TagBuilder
            Return New TagBuilder("input").type(InputTypes.radio).Name(name).value(value)
        End Function

        ''' <summary>
        ''' Create a radio list where the value is mean to be a boolean value.
        ''' </summary>
        ''' <param name="value">True or false. These string value will be translated to true: on, yes, true, 1</param>
        Public Shared Function RadioList(ByVal name As String, ByVal value As Boolean, ByVal items As SelectItemList) As TagBuilder
            For Each item As SelectItem In items.ToArray
                Dim itemBoolValue As Boolean = False
                Select Case item.Value.ToLower
                    Case "on", "yes", "true", "1"
                        itemBoolValue = True
                End Select
                item.Selected = (itemBoolValue = value)
            Next
            Return RadioList(name, items)
        End Function


        Public Shared Function RadioList(ByVal name As String, ByVal value As String, ByVal items As SelectItemList) As TagBuilder
            If String.IsNullOrEmpty(value) = False Then
                For Each item In items.ToArray
                    If item.Value.ToLower = value.ToLower Then item.Selected = True
                Next
            End If
            Return RadioList(name, items)
        End Function

        Public Shared Function RadioList(ByVal name As String, ByVal items As SelectItemList) As TagBuilder
            Dim parent = New TagBuilder("span").ID(name + "Container")

            Dim i = 0
            For Each item In items.ToArray
                ' Add the radio input
                Dim radio = parent.AddChild("input").Name(name).type("radio").value(item.Value).ID(name + i.ToString)
                ' Mark as selected
                If item.Selected Then radio.checked(True)
                ' Add the radio label
                parent.AddChild("label").for(name + i.ToString).AddClass("radioLabel").InnerText(item.Text)
                ' Make the id unique
                i += 1
            Next

            Return parent
        End Function

        Public Shared Function [Select](ByVal name As String) As TagBuilder
            Return New TagBuilder("select").IDandName(name)
        End Function

        Public Shared Function [Select](ByVal name As String, ByVal options() As SelectItem) As TagBuilder
            Return New TagBuilder("select").options(options).IDandName(name)
        End Function

        Public Shared Function [Select](ByVal name As String, ByVal value As Object, ByVal options() As SelectItem) As TagBuilder
            Return New TagBuilder("select").options(options, value).IDandName(name)
        End Function

        Public Shared Function Link(ByVal LinkText As String) As TagBuilder
            Return New TagBuilder("a").InnerText(LinkText).AddAttribute(Attributes.href, "javascript:void(0);")
        End Function

        Public Shared Function LinkOver(ByVal Title As String, ByVal top As Integer, ByVal left As Integer, ByVal width As Integer, ByVal height As Integer) As TagBuilder
            Return New TagBuilder("a").title(Title).AddAttribute(Attributes.href, "javascript:void(0);").absolute(top, left).size(width, height)
        End Function

        Public Shared Function LinkSprite(ByVal Title As String, ByVal img As String, ByVal bgposition As String, ByVal width As Integer, ByVal height As Integer) As TagBuilder
            Return New TagBuilder("a").title(Title).AddAttribute(Attributes.href, "javascript:void(0);").sprite(img, bgposition, width, height).inlineblock
        End Function

        Public Shared Function LinkSpriteRollover(ByVal Title As String, ByVal img As String, ByVal bgposition As String, ByVal width As Integer, ByVal height As Integer, ByVal rolloverposition As String) As TagBuilder
            Return New TagBuilder("a").title(Title).AddAttribute(Attributes.href, "javascript:void(0);").spriteRollover(img, bgposition, width, height, rolloverposition).inlineblock
        End Function

        Public Shared Function Link(ByVal LinkText As String, ByVal href As String) As TagBuilder
            If String.IsNullOrEmpty(href) Then
                Return Link(LinkText)
            Else
                Return New TagBuilder("a").InnerText(LinkText).AddAttribute(Attributes.href, href)
            End If
        End Function

        Public Shared Function Image(ByVal src As String) As TagBuilder
            Return New TagBuilder("img").src(src)
        End Function

        Public Shared Function ImageSprite(ByVal img As String, ByVal bgposition As String, ByVal width As Integer, ByVal height As Integer) As TagBuilder
            Return New TagBuilder("img").src("blank.png").sprite(img, bgposition, width, height)
        End Function

        Public Shared Function ImageSpriteRollover(ByVal img As String, ByVal bgposition As String, ByVal width As Integer, ByVal height As Integer, ByVal rolloverposition As String) As TagBuilder
            Return New TagBuilder("img").src("blank.png").spriteRollover(img, bgposition, width, height, rolloverposition)
        End Function

        Public Shared Function ImageHover(ByVal src As String, ByVal hoversrc As String) As TagBuilder
            Dim setsrcscript = "this.src='{0}'"
            Return New TagBuilder("img").src(src).onmouseover(String.Format(setsrcscript, hoversrc)). _
                                                  onmouseout(String.Format(setsrcscript, src))
        End Function


        Public Shared Function ImageLink(ByVal src As String) As TagBuilder
            Dim lnk = New TagBuilder("a").href("javascript:void(0);").AddClass("pointer")
            lnk.AddChild("img").src(src).AddClass("pointer")
            Return lnk
        End Function

        Public Shared Function ImageLink(ByVal src As String, ByVal url As String) As String
            Return ImageLink(src, url, "")
        End Function

        Public Shared Function ImageLink(ByVal src As String, ByVal url As String, ByVal alt As String) As String
            Return New TagBuilder("a").href(url).InnerHtml(Image(src).Alt(alt).ToString).ToString
        End Function

        Public Shared Function ImageLinkHover(ByVal src As String, ByVal hoversrc As String, ByVal url As String) As String
            Return ImageLinkHover(src, hoversrc, url, "")
        End Function

        Public Shared Function ImageLinkHover(ByVal src As String, ByVal hoversrc As String, ByVal url As String, ByVal alt As String) As String
            Return New TagBuilder("a").href(url).InnerHtml(ImageHover(src, hoversrc).Alt(alt).ToString).ToString
        End Function

        Public Shared Function RenderIf(ByVal condition As Boolean, ByVal content As String) As RenderIfBuilder
            Return New RenderIfBuilder(condition, content)
        End Function

        Public Shared Function RenderIf(ByVal condition As Boolean, ByVal format As String, ByVal ParamArray values As String()) As RenderIfBuilder
            Return New RenderIfBuilder(condition, format, values)
        End Function

        Public Shared Function RenderIfNotEmpty(ByVal data As String) As RenderIfBuilder
            If String.IsNullOrEmpty(data) = False AndAlso data.Trim.Length > 0 Then
                Return New RenderIfBuilder(True, data)
            Else
                Return New RenderIfBuilder(False, data)
            End If
        End Function

        Public Shared Function RenderIfNotEmpty(ByVal data As String, ByVal content As String) As RenderIfBuilder
            If String.IsNullOrEmpty(data) = False AndAlso data.Trim.Length > 0 Then
                Return New RenderIfBuilder(True, content)
            Else
                Return New RenderIfBuilder(False, content)
            End If
        End Function

        Public Shared Function RenderIfNotEmpty(ByVal data As String, ByVal content As String, ByVal ParamArray params() As String) As RenderIfBuilder
            If String.IsNullOrEmpty(data) = False AndAlso data.Trim.Length > 0 Then
                Return New RenderIfBuilder(True, content, params)
            Else
                Return New RenderIfBuilder(False, content, params)
            End If
        End Function

        Public Shared Function GenerateHtmlTable(ByVal data As DataTable) As String
            Dim tb As New StringBuilder
            tb.AppendLine("<table>")

            ' Create the headers
            tb.AppendLine("<thead>")
            tb.AppendLine("<tr>")
            For Each col As DataColumn In data.Columns
                tb.AppendFormat("<th>{0}</th>{1}", col.ColumnName, vbCrLf)
            Next
            tb.AppendLine("</tr>")
            tb.AppendLine("</thead>")

            ' Create the rows
            tb.AppendLine("<tbody>")
            For Each row As DataRow In data.Rows
                tb.AppendLine("<tr>")
                For Each col As DataColumn In data.Columns
                    tb.AppendLine("<td>")
                    If row.IsNull(col) = False Then
                        tb.Append(HttpUtility.HtmlEncode(row(col).ToString))
                    End If
                    tb.AppendLine("</td>" & vbCrLf)
                Next
                tb.AppendLine("</tr>")
            Next
            tb.AppendLine("</tbody>")
            tb.AppendLine("</table>")

            ' Return the data
            Return tb.ToString
        End Function

        Public Class RenderIfBuilder

            Dim _condition As Boolean
            Dim _truecontent As String
            Dim _elsecontent As String
            Dim _containselsecontent As Boolean

            Sub New(ByVal condition As Boolean, ByVal truecontent As String)
                _condition = condition
                _truecontent = truecontent
            End Sub

            Sub New(ByVal condition As Boolean, ByVal trueformat As String, ByVal ParamArray values() As String)
                _condition = condition
                _truecontent = String.Format(trueformat, values)
            End Sub

            Public Function [Else](ByVal elsecontent As String) As RenderIfBuilder
                _containselsecontent = True
                _elsecontent = elsecontent
                Return Me
            End Function

            Public Function [Else](ByVal elseformat As String, ByVal ParamArray params() As String) As RenderIfBuilder
                _containselsecontent = True
                _elsecontent = String.Format(elseformat, params)
                Return Me
            End Function

            Public Overrides Function ToString() As String
                If _condition Then
                    Return _truecontent
                ElseIf _containselsecontent Then
                    Return _elsecontent
                Else
                    Return ""
                End If
            End Function

        End Class

        Public Class Attributes
            Public Const [class] = "class"
            Public Const id = "id"
            Public Const name = "name"
            Public Const alt = "alt"
            Public Const [for] = "for"
            Public Const href = "href"
            Public Const src = "src"
            Public Const style = "style"
            Public Const type = "type"
            Public Const value = "value"
            Public Const onclick = "onclick"
            Public Const onblur = "onblur"
            Public Const onfocus = "onfocus"
            Public Const onmouseover = "onmouseover"
            Public Const onmouseout = "onmouseout"
            Public Const cols = "cols"
            Public Const rows = "rows"
        End Class

        Public Class InputTypes
            Public Const button = "button"
            Public Const checkbox = "checkbox"
            Public Const file = "file"
            Public Const hidden = "hidden"
            Public Const image = "image"
            Public Const password = "password"
            Public Const radio = "radio"
            Public Const reset = "reset"
            Public Const submit = "submit"
            Public Const text = "text"
        End Class

        Public Enum RenderMode
            Normal
            SelfClose
            openTagOnly
        End Enum

        Public Class SelectItem
            Public Selected As Boolean

            Sub New()
            End Sub

            Sub New(ByVal Text As String)
                Me.Value = Text
                Me.Text = Text
            End Sub

            Sub New(ByVal Text As String, ByVal Value As String)
                Me.Value = Value
                Me.Text = Text
            End Sub

            Sub New(ByVal Text As String, ByVal Value As String, ByVal Selected As Boolean)
                Me.Value = Value
                Me.Text = Text
                Me.Selected = Selected
            End Sub


            Private _value As String
            Public Property Value() As String
                Get
                    Return _value
                End Get
                Set(ByVal value As String)
                    _value = value
                End Set
            End Property


            Private _text As String
            Public Property Text() As String
                Get
                    Return _text
                End Get
                Set(ByVal value As String)
                    _text = value
                    If String.IsNullOrEmpty(_value) Then
                        _value = _text
                    End If
                End Set
            End Property


        End Class

        Public Class SelectItemList
            Private _items As New List(Of SelectItem)

            Public Function Add(ByVal text As String) As SelectItemList
                _items.Add(New SelectItem(text))
                Return Me
            End Function
            Public Function Add(ByVal value As String, ByVal text As String) As SelectItemList
                _items.Add(New SelectItem(text, value))
                Return Me
            End Function
            Public Function Add(ByVal value As String, ByVal text As String, ByVal selected As Boolean) As SelectItemList
                _items.Add(New SelectItem(text, value, selected))
                Return Me
            End Function
            Public Function Add(ByVal itemtexts() As String) As SelectItemList
                For Each t In itemtexts
                    _items.Add(New SelectItem(t))
                Next
                Return Me
            End Function
            Public Function ToList() As List(Of SelectItem)
                Return _items
            End Function
            Public Function ToArray() As SelectItem()
                Return _items.ToArray
            End Function
        End Class

        Partial Public Class TagBuilder

            Dim TagName As String
            Dim TagAttributes As New Dictionary(Of String, String)
            Public Mode As New Nullable(Of RenderMode)
            Public Parent As TagBuilder

            ''' <summary>
            ''' Returns the last added child. If you want to access all children use the children collection instead
            ''' </summary>
            Public Child As TagBuilder
            Public Children As New List(Of TagBuilder)

            Private _innerText = ""
            Private _innerHtml = ""
            Private _label = ""
            Private _labelclass As String
            Private _labelWidth As String

            Public Sub New(ByVal tagName As String)
                Me.TagName = tagName
            End Sub

            Public Sub New(ByVal tagName As String, ByVal parent As TagBuilder)
                Me.TagName = tagName
                Me.Parent = parent
            End Sub

            Public Sub New(ByVal tagName As String, ByVal mode As RenderMode)
                Me.TagName = tagName
                Me.Mode = mode
            End Sub

            Public Sub New(ByVal tagName As String, ByVal idAndName As String, ByVal className As String)
                Me.TagName = tagName
                AddAttribute(Attributes.id, idAndName)
                AddAttribute(Attributes.name, idAndName)
                AddAttribute("class", className)
            End Sub

            ''' <summary>
            ''' Adds a child to the children's collection and returns the newaly added child
            ''' </summary>
            ''' <param name="tagname">The tag name for the child to add</param>
            ''' <returns>The added child. Use the Parent property to get back to parent builder</returns>
            Public Function AddChild(ByVal tagname As String) As TagBuilder
                Return AddChild(New TagBuilder(tagname))
            End Function

            Public Function AddChild(ByVal child As TagBuilder) As TagBuilder
                child.Parent = Me
                Children.Add(child)
                Return child
            End Function

            Public Function AddClassIf(ByVal condition As Boolean, ByVal classname As String) As TagBuilder
                Return AddClassIf(condition, classname, String.Empty)
            End Function

            Public Function AddClassIf(ByVal condition As Boolean, ByVal classname As String, ByVal otherclassname As String) As TagBuilder
                If condition Then
                    AddClass(classname)
                ElseIf String.IsNullOrEmpty(otherclassname) = False Then
                    AddClass(otherclassname)
                End If
                Return Me
            End Function

            Public Function AddClass(ByVal ParamArray classes() As String) As TagBuilder
                For Each c In classes
                    MergeAttribute(Attributes.class, c)
                Next
                Return Me
            End Function

            Public Function width(ByVal value As Integer) As TagBuilder
                Return width(value.ToString, value.ToString)
            End Function

            Public Function width(ByVal inputWidth As Integer, ByVal labelWidth As Integer) As TagBuilder
                Return width(inputWidth.ToString, labelWidth.ToString)
            End Function

            Public Function width(ByVal value As String) As TagBuilder
                Return width(value, value)
            End Function

            Public Function width(ByVal inputWidth As String, ByVal labelWidth As String) As TagBuilder
                AppendPixels(inputWidth)
                AppendPixels(labelWidth)
                _labelWidth = labelWidth
                Return MergeAttribute("style", "width: " & inputWidth)
            End Function

            Private Sub AppendPixels(ByRef value As String)
                If value.Trim.EndsWith("%") = False AndAlso value.Trim.EndsWith("px") = False Then
                    value += "px"
                End If
            End Sub

            Public Function labelwidth(ByVal value As String) As TagBuilder
                If value.Trim.EndsWith("%") = False AndAlso value.Trim.EndsWith("px") = False Then
                    value += "px"
                End If
                _labelWidth = value
                Return Me
            End Function

            Public Function height(ByVal value As String) As TagBuilder
                Return MergeAttribute("style", "height: " & value)
            End Function

            Public Function size(ByVal width As Integer, ByVal height As Integer) As TagBuilder
                Return addStyle(String.Format("width: {0}px; height: {1}px", width, height))
            End Function

            Public Function fontsize(ByVal pixels As Integer) As TagBuilder
                Return MergeAttribute("style", "font-size: " & pixels & "px")
            End Function

            Public Function fontsize(ByVal value As String) As TagBuilder
                Return MergeAttribute("style", "font-size: " & value)
            End Function

            Public Function bold() As TagBuilder
                Return MergeAttribute("style", "font-weight: bold")
            End Function

            Public Function padTop(ByVal pixels As Integer) As TagBuilder
                Return addStyle(String.Format("padding-top: {0}px", pixels))
            End Function

            Public Function padLeft(ByVal pixels As Integer) As TagBuilder
                Return addStyle(String.Format("padding-left: {0}px", pixels))
            End Function

            Public Function padRight(ByVal pixels As Integer) As TagBuilder
                Return addStyle(String.Format("padding-right: {0}px", pixels))
            End Function

            Public Function padBottom(ByVal pixels As Integer) As TagBuilder
                Return addStyle(String.Format("padding-bottom: {0}px", pixels))
            End Function

            Public Function pad(ByVal topPx As Integer, ByVal rightPx As Integer, ByVal bottomPx As Integer, ByVal leftPix As Integer) As TagBuilder
                Return addStyle(String.Format("padding: {0}px {1}px {2}px {3}px", topPx, rightPx, bottomPx, leftPix))
            End Function

            Public Function marginTop(ByVal pixels As Integer) As TagBuilder
                Return addStyle(String.Format("margin-top: {0}px", pixels))
            End Function

            Public Function marginLeft(ByVal pixels As Integer) As TagBuilder
                Return addStyle(String.Format("margin-left: {0}px", pixels))
            End Function

            Public Function marginRight(ByVal pixels As Integer) As TagBuilder
                Return addStyle(String.Format("margin-right: {0}px", pixels))
            End Function

            Public Function marginBottom(ByVal pixels As Integer) As TagBuilder
                Return addStyle(String.Format("margin-bottom: {0}px", pixels))
            End Function

            Public Function margin(ByVal topPx As Integer, ByVal rightPx As Integer, ByVal bottomPx As Integer, ByVal leftPix As Integer) As TagBuilder
                Return addStyle(String.Format("margin: {0}px {1}px {2}px {3}px", topPx, rightPx, bottomPx, leftPix))
            End Function

            Public Function inlineblock() As TagBuilder
                Return addStyle("display: inline-block")
            End Function

            Public Function noDecoration() As TagBuilder
                Return MergeAttribute("style", "text-decoration: none")
            End Function

            Public Function color(ByVal value As String) As TagBuilder
                Return MergeAttribute("style", "color: " & value)
            End Function

            Public Function method(ByVal value As String) As TagBuilder
                Return AddAttribute("method", value)
            End Function


            Public Function multipart() As TagBuilder
                Return AddAttribute("enctype", "multipart/form-data")
            End Function

            Public Function action(ByVal url As String) As TagBuilder
                Return AddAttribute("action", url)
            End Function

            Public Function Alt(ByVal value As String) As TagBuilder
                Return AddAttribute(Attributes.alt, value)
            End Function

            Public Function text(ByVal value As String) As TagBuilder
                Return InnerText(value)
            End Function

            Public Function InnerText(ByVal value As String) As TagBuilder
                _innerText = value
                Return Me
            End Function

            Public Function html(ByVal value As String) As TagBuilder
                Return InnerHtml(value)
            End Function

            Public Function InnerHtml(ByVal value As String) As TagBuilder
                _innerHtml = value
                Return Me
            End Function

            Public Function ID(ByVal value As String) As TagBuilder
                Return AddAttribute("id", value)
            End Function

            Public Function Name(ByVal value As String) As TagBuilder
                Return AddAttribute("name", value)
            End Function

            Public Function IDandName(ByVal value As String) As TagBuilder
                ID(value)
                Name(value)
                Return Me
            End Function

            Public Function [for](ByVal name As String) As TagBuilder
                Return AddAttribute(Attributes.for, name)
            End Function

            Public Function href(ByVal value As String) As TagBuilder
                Return AddAttribute(Attributes.href, value)
            End Function

            Public Function type(ByVal inputtype As String) As TagBuilder
                Return AddAttribute(Attributes.type, inputtype)
            End Function

            Public Function value(ByVal val As Object) As TagBuilder
                If TagName.ToLower = "textarea" Then
                    Return InnerText(GetValue(val))
                Else
                    Return AddAttribute(Attributes.value, GetValue(val))
                End If
            End Function

            Private Function GetValue(ByVal val As Object) As String
                If val Is Nothing Then
                    Return ""
                End If
                If val.GetType Is GetType(Integer) AndAlso CType(val, Integer) = Nothing Then
                    Return ""
                End If
                If val.GetType Is GetType(Decimal) AndAlso CType(val, Decimal) = Nothing Then
                    Return ""
                End If
                If val.GetType Is GetType(Double) AndAlso CType(val, Double) = Nothing Then
                    Return ""
                End If
                If val.GetType Is GetType(String) AndAlso String.IsNullOrEmpty(CType(val, String)) Then
                    Return ""
                End If
                If val.GetType Is GetType(Nullable(Of Integer)) AndAlso CType(val, Nullable(Of Integer)).HasValue = False Then
                    Return ""
                End If
                If val.GetType Is GetType(Nullable(Of Decimal)) AndAlso CType(val, Nullable(Of Decimal)).HasValue = False Then
                    Return ""
                End If
                If val.GetType Is GetType(Nullable(Of Double)) AndAlso CType(val, Nullable(Of Double)).HasValue = False Then
                    Return ""
                End If

                ' if a match was not found then shove the tostring in there
                Return val.ToString
            End Function

            Public Function onclick(ByVal value As String) As TagBuilder
                Return AddAttribute(Attributes.onclick, value)
            End Function

            Public Function cancelenter() As TagBuilder
                Return AddAttribute("onkeypress", "if (event.keyCode == 13) { event.cancelBubble = true; return false; }")
            End Function

            Public Function onchange(ByVal value As String) As TagBuilder
                Return AddAttribute("onchange", value)
            End Function

            Public Function onmouseover(ByVal value As String) As TagBuilder
                Return AddAttribute(Attributes.onmouseover, value)
            End Function

            Public Function onmouseout(ByVal value As String) As TagBuilder
                Return AddAttribute(Attributes.onmouseout, value)
            End Function

            Public Function cols(ByVal value As Integer) As TagBuilder
                Return AddAttribute(Attributes.cols, value)
            End Function

            Public Function rows(ByVal value As Integer) As TagBuilder
                Return AddAttribute(Attributes.rows, value)
            End Function

            Public Function style(ByVal value As String) As TagBuilder
                Return AddAttribute(Attributes.style, value)
            End Function

            Public Function addStyle(ByVal value As String) As TagBuilder
                Return MergeAttribute(Attributes.style, value)
            End Function

            Public Function title(ByVal value As String) As TagBuilder
                Return AddAttribute("title", value)
            End Function

            Public Function Label(ByVal value As String) As TagBuilder
                _label = value
                Return Me
            End Function

            Public Function Label(ByVal value As String, ByVal cssclass As String) As TagBuilder
                _label = value
                _labelclass = cssclass
                Return Me
            End Function


            Public Function options(ByVal items() As SelectItem) As TagBuilder
                Return options(items, Nothing)
            End Function

            Public Function options(ByVal items() As SelectItem, ByVal value As Object) As TagBuilder
                Me.value(value)
                Return InnerHtml(GetOptionsString(items, value))
            End Function

            Public Function checked(ByVal value As Boolean) As TagBuilder
                If value = True Then
                    Return AddAttribute("checked", "checked")
                Else
                    Return RemoveAttribute("checked")
                End If
            End Function

            Public Function [readonly]() As TagBuilder
                Return AddAttribute("ReadOnly", "readonly").AddClass("readonlyField")
            End Function

            Public Function src(ByVal url As String) As TagBuilder
                Return AddAttribute(Attributes.src, url)
            End Function

            Public Function Target_Blank() As TagBuilder
                Return AddAttribute("target", "_blank")
            End Function

            Public Function sprite(ByVal img As String, ByVal bgposition As String, ByVal width As Integer, ByVal height As Integer) As TagBuilder
                Return spriteRollover(img, bgposition, width, height, String.Empty)
            End Function

            Public Function spriteRollover(ByVal img As String, ByVal bgposition As String, ByVal width As Integer, ByVal height As Integer, ByVal rolloverposition As String) As TagBuilder
                addStyle(String.Format("background-image:url({0}); background-position: {1}; width: {2}px; height: {3}px;", img, bgposition, width, height))
                If String.IsNullOrEmpty(rolloverposition) = False Then
                    onmouseover("jQuery(this).css('background-position','" + rolloverposition + "');")
                    onmouseout("jQuery(this).css('background-position','" + bgposition + "');")
                End If
                Return Me
            End Function

            Public Function absolute(ByVal top As Integer, ByVal left As Integer) As TagBuilder
                Return addStyle(String.Format("position: absolute; top: {0}px; left: {1}px", top, left))
            End Function

            Public Function absoluteTR(ByVal top As Integer, ByVal right As Integer) As TagBuilder
                Return addStyle(String.Format("position: absolute; top: {0}px; right: {1}px", top, right))
            End Function

            Public Function absoluteBR(ByVal bottom As Integer, ByVal right As Integer) As TagBuilder
                Return addStyle(String.Format("position: absolute; bottom: {0}px; right: {1}px", bottom, right))
            End Function

            Public Function absoluteBL(ByVal bottom As Integer, ByVal left As Integer) As TagBuilder
                Return addStyle(String.Format("position: absolute; bottom: {0}px; left: {1}px", bottom, left))
            End Function

            Public Function causesValidation() As TagBuilder
                Return AddClass("causesValidation")
            End Function

            Public Function ValRequired() As TagBuilder
                Return AddClass("required")
            End Function

            Public Function ValRemote(ByVal remoteUrl As String) As TagBuilder
                Return AddAttribute("remote", remoteUrl)
            End Function

            Public Function ValMinLength(ByVal minLength As Integer) As TagBuilder
                Return AddAttribute("minlength", minLength)
            End Function

            Public Function ValMaxLength(ByVal maxLength As Integer) As TagBuilder
                Return AddAttribute("maxlength", maxLength)
            End Function

            Public Function ValRangeLength(ByVal RangeStart As Integer, ByVal RangeEnd As Integer) As TagBuilder
                Return AddAttribute("rangelength", String.Format("[{0},{1}]", RangeStart, RangeEnd))
            End Function

            Public Function ValMin(ByVal min As Integer) As TagBuilder
                Return AddAttribute("min", min)
            End Function

            Public Function ValMax(ByVal max As Integer) As TagBuilder
                Return AddAttribute("max", max)
            End Function

            Public Function ValRange(ByVal start As Integer, ByVal [end] As Integer) As TagBuilder
                Return AddAttribute("range", String.Format("[{0},{1}]", start, [end]))
            End Function

            Public Function ValEmail() As TagBuilder
                Return AddClass("email")
            End Function

            Public Function ValUrl() As TagBuilder
                Return AddClass("url")
            End Function

            Public Function ValDate() As TagBuilder
                Return AddClass("date")
            End Function

            Public Function ValNumber() As TagBuilder
                Return AddClass("number")
            End Function

            Public Function ValCreditCard() As TagBuilder
                Return AddClass("creditcard")
            End Function

            Public Function ValDigits() As TagBuilder
                Return AddClass("digits")
            End Function

            Public Function ValAcceptExt(ByVal ParamArray extensions() As String) As TagBuilder
                Dim extString = ""
                For Each e In extensions
                    If extString.Length > 0 Then extString += "|"
                    extString += e
                Next
                Return AddAttribute("accept", extString)
            End Function

            Public Function ValEqualTo(ByVal fieldname As String) As TagBuilder
                If fieldname.StartsWith("#") = False Then
                    fieldname = "#" + fieldname
                End If
                Return AddAttribute("equalTo", fieldname)
            End Function

            Public Function ValPassword() As TagBuilder
                Return ValRegExp("^.*(?=.{7,})((?=.*\d)(?=.*[a-z])|(?=.*\d)(?=.*[a-z])(?=.*[^a-zA-Z0-9])|(?=.*\d)(?=.*[^a-zA-Z0-9])|(?=.*[a-z])(?=.*[^a-zA-Z0-9])).*$", _
                                 "Password must be at least 7 characters, must contain a letter and a number, and must also contain one or more non-alphanumeric character.")
            End Function

            Public Function ValRegExp(ByVal expression As String, ByVal message As String) As TagBuilder
                AddAttribute("regExp", expression)
                Return ValMessage("regExp", message)
            End Function

            Private _valMessages As New Dictionary(Of String, String)
            Public Function ValMessage(ByVal validator As String, ByVal message As String) As TagBuilder
                _valMessages.Add(validator, message)
                Return Me
            End Function

            Public Function attr(ByVal key As String, ByVal value As Integer) As TagBuilder
                Return AddAttribute(key, value)
            End Function

            Public Function attr(ByVal key As String, ByVal value As String) As TagBuilder
                Return AddAttribute(key, value)
            End Function

            Public Function AddAttribute(ByVal key As String, ByVal value As String) As TagBuilder
                If TagAttributes.ContainsKey(key) Then
                    TagAttributes(key) = value
                Else
                    TagAttributes.Add(key, value)
                End If
                Return Me
            End Function

            Public Function RemoveAttribute(ByVal key As String) As TagBuilder
                If TagAttributes.ContainsKey(key) Then
                    TagAttributes.Remove(key)
                End If
                Return Me
            End Function



            Public Function MergeAttribute(ByVal key As String, ByVal value As String) As TagBuilder
                If TagAttributes.ContainsKey(key) Then

                    ' Merge style attributes with a ;
                    If key = Attributes.style Then
                        Dim currentStyle As String = GetAttribute(Attributes.style).Trim

                        If currentStyle.Length > 0 Then
                            If currentStyle.EndsWith(";") = False Then currentStyle += "; "
                            currentStyle += value
                        Else
                            currentStyle = value
                        End If

                        TagAttributes(key) = currentStyle

                    Else ' add a space for all other merging operations
                        TagAttributes(key) += " " & value

                    End If
                Else
                    TagAttributes.Add(key, value)
                End If
                Return Me
            End Function

            Public Function GetAttribute(ByVal attrName As String) As String
                If TagAttributes.ContainsKey(attrName) = False Then Return String.Empty
                Return TagAttributes(attrName)
            End Function


            Private Function GetAttributeString() As String
                Dim attributeFormat As String = " {0}=""{1}"""
                Dim value As String = ""
                For Each key As String In TagAttributes.Keys
                    value += String.Format(attributeFormat, key, TagAttributes(key))
                Next
                Return value
            End Function

            Private Function GetLabelString() As String
                If String.IsNullOrEmpty(_label) = False Then
                    Dim lbl = New TagBuilder("label")
                    If String.IsNullOrEmpty(GetAttribute("name")) = False Then
                        lbl.for(GetAttribute("name"))
                    End If
                    If String.IsNullOrEmpty(_labelclass) = False Then
                        lbl.AddClass(_labelclass)
                    Else
                        lbl.AddClass("fieldlabel")
                    End If
                    If String.IsNullOrEmpty(_labelWidth) = False Then
                        lbl.width(_labelWidth)
                    End If
                    lbl.InnerText(_label)
                    Return vbCrLf & lbl.ToString & vbCrLf
                Else
                    Return ""
                End If
            End Function

            Private Function GetOptionsString(ByVal items As SelectItem(), ByVal val As String) As String
                Dim optionFormat = " <option value=""{0}"" {2}>{1}</option>" '' selected="selected""
                Dim selectedString = " selected=""selected"""
                Dim sb As New StringBuilder
                Dim valueFound = False
                For Each item In items
                    Dim selected = False
                    If item.Selected = True Then
                        selected = True
                        valueFound = True
                    ElseIf String.IsNullOrEmpty(val) = False AndAlso item.Value.ToUpper = val.ToUpper Then
                        selected = True
                        valueFound = True
                    End If
                    sb.AppendLine(String.Format(optionFormat, item.Value, item.Text, IIf(selected, selectedString, "")))
                Next

                ' If the value was not found in the options then add it.
                If String.IsNullOrEmpty(val) = False AndAlso valueFound = False Then
                    sb.AppendLine(String.Format(optionFormat, val, val, selectedString))
                End If

                Return sb.ToString
            End Function

            Public Overrides Function ToString() As String
                Return ToString(IIf(Mode.HasValue, Mode, RenderMode.Normal))
            End Function

            Public Overloads Function ToString(ByVal mode As RenderMode) As String
                Dim normalTagFormat = "<{0}{1}>{2}</{0}>"
                Dim selfCloseTagFormat = "<{0}{1} />"
                Dim openTagFormat = "<{0}{1}>{2}"

                ' Create the validation messages string
                Dim valData = ""
                For Each m As String In _valMessages.Keys
                    If valData.Length > 0 Then valData += ", "
                    valData += m + ": '" & _valMessages(m) & "'"
                Next
                If valData.Length > 0 Then AddClass("{messages: {" & valData & "}}")

                ' Create the inner value
                Dim innerValue As String = IIf(String.IsNullOrEmpty(_innerHtml), _
                                               HttpUtility.HtmlEncode(_innerText), _
                                               _innerHtml)

                ' Render the children into the inner html of the element
                For Each c As TagBuilder In Children
                    innerValue += c.ToString
                Next

                Select Case TagName.ToLower
                    Case "input"
                        mode = RenderMode.SelfClose
                    Case "img"
                        mode = RenderMode.SelfClose
                End Select

                ' Create the tag string
                Dim tagstring = ""
                If mode = RenderMode.SelfClose AndAlso String.IsNullOrEmpty(innerValue) Then
                    tagstring = String.Format(selfCloseTagFormat, TagName, GetAttributeString)
                ElseIf mode = RenderMode.openTagOnly Then
                    tagstring = String.Format(openTagFormat, TagName, GetAttributeString, innerValue)
                Else
                    tagstring = String.Format(normalTagFormat, TagName, GetAttributeString, innerValue)
                End If

                ' Return the label (if found) and tag string
                Return GetLabelString() & tagstring
            End Function

        End Class
    End Class


End Namespace
